{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sequential Variational Autoencoders for Collaborative Filtering\n",
    "\n",
    "**Noveen Sachdeva, Giuseppe Manco, Ettore Ritacco, and Vikram Pudi** - *12th International ACM Conference on Web Search and Data Mining - WSDM '19*\n",
    "\n",
    "The notebook provides PyTorch code for the proposed model, \"SVAE\" along with the data preprocessing for the Movielens-1M dataset."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torch.autograd import Variable\n",
    "\n",
    "import os\n",
    "import time\n",
    "import json\n",
    "import pickle\n",
    "import random\n",
    "import functools\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from tqdm import tqdm\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Hyper Parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "### change `DATA_DIR` to the location where the dataset sits\n",
    "### compatible datasets: ML-1M, Netflix-full\n",
    "\n",
    "hyper_params = {\n",
    "    'data_base': 'saved_data/ml-1m/', # Don't remove the '/' at the end please :)\n",
    "    'project_name': 'svae_ml1m',\n",
    "    # 'data_base': 'saved_data/netflix-full/',\n",
    "    # 'project_name': 'svae_netflix_full',\n",
    "    'model_file_name': '',\n",
    "    'log_file': '',\n",
    "    'history_split_test': [0.8, 0.2], # Part of test history to train on : Part of test history to test\n",
    "\n",
    "    'learning_rate': 0.01, # learning rate is required only if optimizer is adagrad\n",
    "    'optimizer': 'adam',\n",
    "    'weight_decay': float(5e-3),\n",
    "\n",
    "    'epochs': 25,\n",
    "    'batch_size': 1, # Needs to be 1, because we don't pack multiple sequences in the same batch\n",
    "    \n",
    "    'item_embed_size': 256,\n",
    "    'rnn_size': 200,\n",
    "    'hidden_size': 150,\n",
    "    'latent_size': 64,\n",
    "    'loss_type': 'next_k', # [predict_next, same, prefix, postfix, exp_decay, next_k]\n",
    "    'next_k': 4,\n",
    "\n",
    "    'number_users_to_keep': 1000000000,\n",
    "    'batch_log_interval': 1000,\n",
    "    'train_cp_users': 200,\n",
    "    'exploding_clip': 0.25,\n",
    "}\n",
    "\n",
    "file_name = '_optimizer_' + str(hyper_params['optimizer'])\n",
    "if hyper_params['optimizer'] == 'adagrad':\n",
    "    file_name += '_lr_' + str(hyper_params['learning_rate'])\n",
    "file_name += '_weight_decay_' + str(hyper_params['weight_decay'])\n",
    "file_name += '_loss_type_' + str(hyper_params['loss_type'])\n",
    "file_name += '_item_embed_size_' + str(hyper_params['item_embed_size'])\n",
    "file_name += '_rnn_size_' + str(hyper_params['rnn_size'])\n",
    "file_name += '_latent_size_' + str(hyper_params['latent_size'])\n",
    "\n",
    "log_file_root = \"saved_logs/\" # Don't remove the '/' at the end please :)\n",
    "model_file_root = \"saved_models/\" # Don't remove the '/' at the end please :)\n",
    "\n",
    "if not os.path.isdir(log_file_root): os.mkdir(log_file_root)\n",
    "if not os.path.isdir(model_file_root): os.mkdir(model_file_root)\n",
    "hyper_params['log_file'] = log_file_root + hyper_params['project_name'] + '_log' + file_name + '.txt'\n",
    "hyper_params['model_file_name'] = model_file_root + hyper_params['project_name'] + '_model' + file_name + '.pt'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data Preprocessing\n",
    "\n",
    "**Courtesy:** Dawen Liang et al. \"*Variational autoencoders for collaborative filtering*\" published at WWW '18. <br>\n",
    "**Link:** https://github.com/dawenl/vae_cf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATA_DIR = hyper_params['data_base']\n",
    "pro_dir = os.path.join(DATA_DIR, 'pro_sg') # Path where preprocessed data will be saved\n",
    "hyper_params['data_base'] += 'pro_sg/'\n",
    "\n",
    "if not os.path.isdir(pro_dir): # We don't want to keep preprocessing every time we run the notebook\n",
    "    cols = ['userId', 'movieId', 'rating', 'timestamp']\n",
    "    dtypes = {'userId': 'int', 'movieId': 'int', 'timestamp': 'int', 'rating': 'int'}\n",
    "    raw_data = pd.read_csv(os.path.join(DATA_DIR, 'ratings.dat'), sep='::', names=cols, parse_dates=['timestamp'])\n",
    "\n",
    "    max_seq_len = 1000\n",
    "    n_heldout_users = 750 # If total users = N; train_users = N - 2*heldout; test_users & val_users = heldout\n",
    "\n",
    "    # binarize the data (only keep ratings >= 4)\n",
    "    raw_data = raw_data[raw_data['rating'] > 3.5]\n",
    "\n",
    "    # Remove users with greater than $max_seq_len number of watched movies\n",
    "    raw_data = raw_data.groupby([\"userId\"]).filter(lambda x: len(x) <= max_seq_len)\n",
    "\n",
    "    # Sort data values with the timestamp\n",
    "    raw_data = raw_data.groupby([\"userId\"]).apply(lambda x: x.sort_values([\"timestamp\"], ascending = True)).reset_index(drop=True)\n",
    "\n",
    "    raw_data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_count(tp, id):\n",
    "    playcount_groupbyid = tp[[id]].groupby(id, as_index=False)\n",
    "    count = playcount_groupbyid.size()\n",
    "    return count\n",
    "\n",
    "def filter_triplets(tp, min_uc=5, min_sc=0):\n",
    "    # Only keep the triplets for items which were clicked on by at least min_sc users. \n",
    "    if min_sc > 0:\n",
    "        itemcount = get_count(tp, 'movieId')\n",
    "        tp = tp[tp['movieId'].isin(itemcount.index[itemcount >= min_sc])]\n",
    "    \n",
    "    # Only keep the triplets for users who clicked on at least min_uc items\n",
    "    # After doing this, some of the items will have less than min_uc users, but should only be a small proportion\n",
    "    if min_uc > 0:\n",
    "        usercount = get_count(tp, 'userId')\n",
    "        tp = tp[tp['userId'].isin(usercount.index[usercount >= min_uc])]\n",
    "    \n",
    "    # Update both usercount and itemcount after filtering\n",
    "    usercount, itemcount = get_count(tp, 'userId'), get_count(tp, 'movieId') \n",
    "    return tp, usercount, itemcount\n",
    "\n",
    "def split_train_test_proportion(data, test_prop=0.2):\n",
    "    data_grouped_by_user = data.groupby('userId')\n",
    "    tr_list, te_list = list(), list()\n",
    "\n",
    "    np.random.seed(98765)\n",
    "\n",
    "    for i, (_, group) in enumerate(data_grouped_by_user):\n",
    "        n_items_u = len(group)\n",
    "\n",
    "        if n_items_u >= 5:\n",
    "            idx = np.zeros(n_items_u, dtype='bool')\n",
    "            # idx[np.random.choice(n_items_u, size=int(test_prop * n_items_u), replace=False).astype('int64')] = True\n",
    "            idx[int((1.0 - test_prop) * n_items_u):] = True\n",
    "            # print(idx)\n",
    "            \n",
    "            tr_list.append(group[np.logical_not(idx)])\n",
    "            te_list.append(group[idx])\n",
    "        else:\n",
    "            tr_list.append(group)\n",
    "\n",
    "        if i % 1000 == 0:\n",
    "            print(\"%d users sampled\" % i)\n",
    "            sys.stdout.flush()\n",
    "\n",
    "    data_tr = pd.concat(tr_list)\n",
    "    data_te = pd.concat(te_list)\n",
    "    \n",
    "    return data_tr, data_te\n",
    "\n",
    "def numerize(tp):\n",
    "    uid = list(map(lambda x: profile2id[x], tp['userId']))\n",
    "    sid = list(map(lambda x: show2id[x], tp['movieId']))\n",
    "    ra = list(map(lambda x: x, tp['rating']))\n",
    "    ret =  pd.DataFrame(data={'uid': uid, 'sid': sid, 'rating': ra}, columns=['uid', 'sid', 'rating'])\n",
    "    ret['rating'] = ret['rating'].apply(pd.to_numeric)\n",
    "    return ret"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not os.path.isdir(pro_dir): # We don't want to keep preprocessing every time we run the notebook\n",
    "\n",
    "    raw_data, user_activity, item_popularity = filter_triplets(raw_data)\n",
    "\n",
    "    sparsity = 1. * raw_data.shape[0] / (user_activity.shape[0] * item_popularity.shape[0])\n",
    "\n",
    "    print(\"After filtering, there are %d watching events from %d users and %d movies (sparsity: %.3f%%)\" % \n",
    "          (raw_data.shape[0], user_activity.shape[0], item_popularity.shape[0], sparsity * 100))\n",
    "\n",
    "    unique_uid = user_activity.index\n",
    "\n",
    "    np.random.seed(98765)\n",
    "    idx_perm = np.random.permutation(unique_uid.size)\n",
    "    unique_uid = unique_uid[idx_perm]\n",
    "\n",
    "    # create train/validation/test users\n",
    "    n_users = unique_uid.size\n",
    "\n",
    "    tr_users = unique_uid[:(n_users - n_heldout_users * 2)]\n",
    "    vd_users = unique_uid[(n_users - n_heldout_users * 2): (n_users - n_heldout_users)]\n",
    "    te_users = unique_uid[(n_users - n_heldout_users):]\n",
    "\n",
    "    train_plays = raw_data.loc[raw_data['userId'].isin(tr_users)]\n",
    "\n",
    "    unique_sid = pd.unique(train_plays['movieId'])\n",
    "\n",
    "    show2id = dict((sid, i) for (i, sid) in enumerate(unique_sid))\n",
    "    profile2id = dict((pid, i) for (i, pid) in enumerate(unique_uid))\n",
    "\n",
    "    if not os.path.exists(pro_dir):\n",
    "        os.makedirs(pro_dir)\n",
    "\n",
    "    with open(os.path.join(pro_dir, 'unique_sid.txt'), 'w') as f:\n",
    "        for sid in unique_sid:\n",
    "            f.write('%s\\n' % sid)\n",
    "\n",
    "    vad_plays = raw_data.loc[raw_data['userId'].isin(vd_users)]\n",
    "    vad_plays = vad_plays.loc[vad_plays['movieId'].isin(unique_sid)]\n",
    "\n",
    "    vad_plays_tr, vad_plays_te = split_train_test_proportion(vad_plays)\n",
    "\n",
    "    test_plays = raw_data.loc[raw_data['userId'].isin(te_users)]\n",
    "    test_plays = test_plays.loc[test_plays['movieId'].isin(unique_sid)]\n",
    "\n",
    "    test_plays_tr, test_plays_te = split_train_test_proportion(test_plays)\n",
    "\n",
    "    train_data = numerize(train_plays)\n",
    "    train_data.to_csv(os.path.join(pro_dir, 'train.csv'), index=False)\n",
    "\n",
    "    vad_data_tr = numerize(vad_plays_tr)\n",
    "    vad_data_tr.to_csv(os.path.join(pro_dir, 'validation_tr.csv'), index=False)\n",
    "\n",
    "    vad_data_te = numerize(vad_plays_te)\n",
    "    vad_data_te.to_csv(os.path.join(pro_dir, 'validation_te.csv'), index=False)\n",
    "\n",
    "    test_data_tr = numerize(test_plays_tr)\n",
    "    test_data_tr.to_csv(os.path.join(pro_dir, 'test_tr.csv'), index=False)\n",
    "\n",
    "    test_data_te = numerize(test_plays_te)\n",
    "    test_data_te.to_csv(os.path.join(pro_dir, 'test_te.csv'), index=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Utlity functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using CUDA...\n",
      "\n"
     ]
    }
   ],
   "source": [
    "LongTensor = torch.LongTensor\n",
    "FloatTensor = torch.FloatTensor\n",
    "\n",
    "is_cuda_available = torch.cuda.is_available()\n",
    "\n",
    "if is_cuda_available: \n",
    "    print(\"Using CUDA...\\n\")\n",
    "    LongTensor = torch.cuda.LongTensor\n",
    "    FloatTensor = torch.cuda.FloatTensor\n",
    "    \n",
    "def save_obj(obj, name):\n",
    "    with open(name + '.pkl', 'wb') as f:\n",
    "        pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)\n",
    "\n",
    "def save_obj_json(obj, name):\n",
    "    with open(name + '.json', 'w') as f:\n",
    "        json.dump(obj, f)\n",
    "\n",
    "def load_obj(name):\n",
    "    with open(name + '.pkl', 'rb') as f:\n",
    "        return pickle.load(f)\n",
    "\n",
    "def load_obj_json(name):\n",
    "    with open(name + '.json', 'r') as f:\n",
    "        return json.load(f)\n",
    "\n",
    "def file_write(log_file, s):\n",
    "    print(s)\n",
    "    f = open(log_file, 'a')\n",
    "    f.write(s+'\\n')\n",
    "    f.close()\n",
    "\n",
    "def clear_log_file(log_file):\n",
    "    f = open(log_file, 'w')\n",
    "    f.write('')\n",
    "    f.close()\n",
    "\n",
    "def pretty_print(h):\n",
    "    print(\"{\")\n",
    "    for key in h:\n",
    "        print(' ' * 4 + str(key) + ': ' + h[key])\n",
    "    print('}\\n')\n",
    "    \n",
    "def plot_len_vs_ndcg(len_to_ndcg_at_100_map):\n",
    "    \n",
    "    lens = list(len_to_ndcg_at_100_map.keys())\n",
    "    lens.sort()\n",
    "    X, Y = [], []\n",
    "    \n",
    "    for le in lens:\n",
    "        X.append(le)\n",
    "        ans = 0.0\n",
    "        for i in len_to_ndcg_at_100_map[le]: ans += float(i)\n",
    "        ans = ans / float(len(len_to_ndcg_at_100_map[le]))\n",
    "        Y.append(ans * 100.0)\n",
    "    \n",
    "    # Smoothening\n",
    "    Y_mine = []\n",
    "    prev_5 = []\n",
    "    for i in Y:\n",
    "        prev_5.append(i)\n",
    "        if len(prev_5) > 5: del prev_5[0]\n",
    "\n",
    "        temp = 0.0\n",
    "        for j in prev_5: temp += float(j)\n",
    "        temp = float(temp) / float(len(prev_5))\n",
    "        Y_mine.append(temp)\n",
    "    \n",
    "    plt.figure(figsize=(12, 5))\n",
    "    plt.plot(X, Y_mine, label='SVAE')\n",
    "    plt.xlabel(\"Number of items in the fold-out set\")\n",
    "    plt.ylabel(\"Average NDCG@100\")\n",
    "    plt.title(hyper_params['project_name'])\n",
    "    if not os.path.isdir(\"saved_plots/\"): os.mkdir(\"saved_plots/\")\n",
    "    plt.savefig(\"saved_plots/seq_len_vs_ndcg_\" + hyper_params['project_name'] + \".pdf\")\n",
    "\n",
    "    leg = plt.legend(loc='best', ncol=2)\n",
    "    \n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data Parsing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "def load_data(hyper_params):\n",
    "    \n",
    "    file_write(hyper_params['log_file'], \"Started reading data file\")\n",
    "    \n",
    "    f = open(hyper_params['data_base'] + 'train.csv')\n",
    "    lines_train = f.readlines()[1:]\n",
    "    \n",
    "    f = open(hyper_params['data_base'] + 'validation_tr.csv')\n",
    "    lines_val_tr = f.readlines()[1:]\n",
    "    \n",
    "    f = open(hyper_params['data_base'] + 'validation_te.csv')\n",
    "    lines_val_te = f.readlines()[1:]\n",
    "    \n",
    "    f = open(hyper_params['data_base'] + 'test_tr.csv')\n",
    "    lines_test_tr = f.readlines()[1:]\n",
    "    \n",
    "    f = open(hyper_params['data_base'] + 'test_te.csv')\n",
    "    lines_test_te = f.readlines()[1:]\n",
    "    \n",
    "    unique_sid = list()\n",
    "    with open(hyper_params['data_base'] + 'unique_sid.txt', 'r') as f:\n",
    "        for line in f:\n",
    "            unique_sid.append(line.strip())\n",
    "    num_items = len(unique_sid)\n",
    "    \n",
    "    file_write(hyper_params['log_file'], \"Data Files loaded!\")\n",
    "\n",
    "    train_reader = DataReader(hyper_params, lines_train, None, num_items, True)\n",
    "    val_reader = DataReader(hyper_params, lines_val_tr, lines_val_te, num_items, False)\n",
    "    test_reader = DataReader(hyper_params, lines_test_tr, lines_test_te, num_items, False)\n",
    "\n",
    "    return train_reader, val_reader, test_reader, num_items\n",
    "\n",
    "class DataReader:\n",
    "    def __init__(self, hyper_params, a, b, num_items, is_training):\n",
    "        self.hyper_params = hyper_params\n",
    "        self.batch_size = hyper_params['batch_size']\n",
    "        \n",
    "        num_users = 0\n",
    "        min_user = 1000000000000000000000000 # Infinity\n",
    "        for line in a:\n",
    "            line = line.strip().split(\",\")\n",
    "            num_users = max(num_users, int(line[0]))\n",
    "            min_user = min(min_user, int(line[0]))\n",
    "        num_users = num_users - min_user + 1\n",
    "        \n",
    "        self.num_users = num_users\n",
    "        self.min_user = min_user\n",
    "        self.num_items = num_items\n",
    "        \n",
    "        self.data_train = a\n",
    "        self.data_test = b\n",
    "        self.is_training = is_training\n",
    "        self.all_users = []\n",
    "        \n",
    "        self.prep()\n",
    "        self.number()\n",
    "\n",
    "    def prep(self):\n",
    "        self.data = []\n",
    "        for i in range(self.num_users): self.data.append([])\n",
    "            \n",
    "        for i in tqdm(range(len(self.data_train))):\n",
    "            line = self.data_train[i]\n",
    "            line = line.strip().split(\",\")\n",
    "            self.data[int(line[0]) - self.min_user].append([ int(line[1]), 1 ])\n",
    "        \n",
    "        if self.is_training == False:\n",
    "            self.data_te = []\n",
    "            for i in range(self.num_users): self.data_te.append([])\n",
    "                \n",
    "            for i in tqdm(range(len(self.data_test))):\n",
    "                line = self.data_test[i]\n",
    "                line = line.strip().split(\",\")\n",
    "                self.data_te[int(line[0]) - self.min_user].append([ int(line[1]), 1 ])\n",
    "        \n",
    "    def number(self):\n",
    "        self.num_b = int(min(len(self.data), self.hyper_params['number_users_to_keep']) / self.batch_size)\n",
    "    \n",
    "    def iter(self):\n",
    "        users_done = 0\n",
    "\n",
    "        x_batch = []\n",
    "        \n",
    "        user_iterate_order = list(range(len(self.data)))\n",
    "        \n",
    "        # Randomly shuffle the training order\n",
    "        np.random.shuffle(user_iterate_order)\n",
    "        \n",
    "        for user in user_iterate_order:\n",
    "\n",
    "            if users_done > self.hyper_params['number_users_to_keep']: break\n",
    "            users_done += 1\n",
    "            \n",
    "            y_batch_s = torch.zeros(self.batch_size, len(self.data[user]) - 1, self.num_items)\n",
    "            if is_cuda_available: y_batch_s = y_batch_s.cuda()\n",
    "            \n",
    "            if self.hyper_params['loss_type'] == 'predict_next':\n",
    "                for timestep in range(len(self.data[user]) - 1):\n",
    "                    y_batch_s[len(x_batch), timestep, :].scatter_(\n",
    "                        0, LongTensor([ i[0] for i in [ self.data[user][timestep + 1] ] ]), 1.0\n",
    "                    )\n",
    "                \n",
    "            elif self.hyper_params['loss_type'] == 'next_k':\n",
    "                for timestep in range(len(self.data[user]) - 1):\n",
    "                    y_batch_s[len(x_batch), timestep, :].scatter_(\n",
    "                        0, LongTensor([ i[0] for i in self.data[user][timestep + 1:][:self.hyper_params['next_k']] ]), 1.0\n",
    "                    )\n",
    "                \n",
    "            elif self.hyper_params['loss_type'] == 'postfix':\n",
    "                for timestep in range(len(self.data[user]) - 1):\n",
    "                    y_batch_s[len(x_batch), timestep, :].scatter_(\n",
    "                        0, LongTensor([ i[0] for i in self.data[user][timestep + 1:] ]), 1.0\n",
    "                    )\n",
    "            \n",
    "            x_batch.append([ i[0] for i in self.data[user][:-1] ])\n",
    "            \n",
    "            if len(x_batch) == self.batch_size: # batch_size always = 1\n",
    "            \n",
    "                yield Variable(LongTensor(x_batch)), Variable(y_batch_s, requires_grad=False)\n",
    "                x_batch = []\n",
    "\n",
    "    def iter_eval(self):\n",
    "\n",
    "        x_batch = []\n",
    "        test_movies, test_movies_r = [], []\n",
    "        \n",
    "        users_done = 0\n",
    "        \n",
    "        for user in range(len(self.data)):\n",
    "            \n",
    "            users_done += 1\n",
    "            if users_done > self.hyper_params['number_users_to_keep']: break\n",
    "            \n",
    "            if self.is_training == True: \n",
    "                split = float(self.hyper_params['history_split_test'][0])\n",
    "                base_predictions_on = self.data[user][:int(split * len(self.data[user]))]\n",
    "                heldout_movies = self.data[user][int(split * len(self.data[user])):]\n",
    "            else:\n",
    "                base_predictions_on = self.data[user]\n",
    "                heldout_movies = self.data_te[user]\n",
    "            \n",
    "            y_batch_s = torch.zeros(self.batch_size, len(base_predictions_on) - 1, self.num_items).cuda()\n",
    "            \n",
    "            if self.hyper_params['loss_type'] == 'predict_next':\n",
    "                for timestep in range(len(base_predictions_on) - 1):\n",
    "                    y_batch_s[len(x_batch), timestep, :].scatter_(\n",
    "                        0, LongTensor([ i[0] for i in [ base_predictions_on[timestep + 1] ] ]), 1.0\n",
    "                    )\n",
    "                \n",
    "            elif self.hyper_params['loss_type'] == 'next_k':\n",
    "                for timestep in range(len(base_predictions_on) - 1):\n",
    "                    y_batch_s[len(x_batch), timestep, :].scatter_(\n",
    "                        0, LongTensor([ i[0] for i in base_predictions_on[timestep + 1:][:self.hyper_params['next_k']] ]), 1.0\n",
    "                    )\n",
    "                \n",
    "            elif self.hyper_params['loss_type'] == 'postfix':\n",
    "                for timestep in range(len(base_predictions_on) - 1):\n",
    "                    y_batch_s[len(x_batch), timestep, :].scatter_(\n",
    "                        0, LongTensor([ i[0] for i in base_predictions_on[timestep + 1:] ]), 1.0\n",
    "                    )\n",
    "            \n",
    "            test_movies.append([ i[0] for i in heldout_movies ])\n",
    "            test_movies_r.append([ i[1] for i in heldout_movies ])\n",
    "            x_batch.append([ i[0] for i in base_predictions_on[:-1] ])\n",
    "            \n",
    "            if len(x_batch) == self.batch_size: # batch_size always = 1\n",
    "                \n",
    "                yield Variable(LongTensor(x_batch)), Variable(y_batch_s, requires_grad=False), test_movies, test_movies_r\n",
    "                x_batch = []\n",
    "                test_movies, test_movies_r = [], []"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Evaluation Code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "def evaluate(model, criterion, reader, hyper_params, is_train_set):\n",
    "    model.eval()\n",
    "\n",
    "    metrics = {}\n",
    "    metrics['loss'] = 0.0\n",
    "    Ks = [10, 100]\n",
    "    for k in Ks: \n",
    "        metrics['NDCG@' + str(k)] = 0.0\n",
    "        metrics['Rec@' + str(k)] = 0.0\n",
    "        metrics['Prec@' + str(k)] = 0.0\n",
    "\n",
    "    batch = 0\n",
    "    total_users = 0.0\n",
    "    \n",
    "    # For plotting the results (seq length vs. NDCG@100)\n",
    "    len_to_ndcg_at_100_map = {}\n",
    "\n",
    "    for x, y_s, test_movies, test_movies_r in reader.iter_eval():\n",
    "        batch += 1\n",
    "        if is_train_set == True and batch > hyper_params['train_cp_users']: break\n",
    "\n",
    "        decoder_output, z_mean, z_log_sigma = model(x)\n",
    "        \n",
    "        metrics['loss'] += criterion(decoder_output, z_mean, z_log_sigma, y_s, 0.2).data[0]\n",
    "        \n",
    "        # Making the logits of previous items in the sequence to be \"- infinity\"\n",
    "        decoder_output = decoder_output.data\n",
    "        x_scattered = torch.zeros(decoder_output.shape[0], decoder_output.shape[2])\n",
    "        if is_cuda_available: x_scattered = x_scattered.cuda()\n",
    "        x_scattered[0, :].scatter_(0, x[0].data, 1.0)\n",
    "        last_predictions = decoder_output[:, -1, :] - (torch.abs(decoder_output[:, -1, :] * x_scattered) * 100000000)\n",
    "        \n",
    "        for batch_num in range(last_predictions.shape[0]): # batch_num is ideally only 0, since batch_size is enforced to be always 1\n",
    "            predicted_scores = last_predictions[batch_num]\n",
    "            actual_movies_watched = test_movies[batch_num]\n",
    "            actual_movies_ratings = test_movies_r[batch_num]\n",
    "                    \n",
    "            # Calculate NDCG\n",
    "            _, argsorted = torch.sort(-1.0 * predicted_scores)\n",
    "            for k in Ks:\n",
    "                best, now_at, dcg, hits = 0.0, 0.0, 0.0, 0.0\n",
    "                \n",
    "                rec_list = list(argsorted[:k].cpu().numpy())\n",
    "                for m in range(len(actual_movies_watched)):\n",
    "                    movie = actual_movies_watched[m]\n",
    "                    now_at += 1.0\n",
    "                    if now_at <= k: best += 1.0 / float(np.log2(now_at + 1))\n",
    "                    \n",
    "                    if movie not in rec_list: continue\n",
    "                    hits += 1.0\n",
    "                    dcg += 1.0 / float(np.log2(float(rec_list.index(movie) + 2)))\n",
    "                \n",
    "                metrics['NDCG@' + str(k)] += float(dcg) / float(best)\n",
    "                metrics['Rec@' + str(k)] += float(hits) / float(len(actual_movies_watched))\n",
    "                metrics['Prec@' + str(k)] += float(hits) / float(k)\n",
    "                \n",
    "                # Only for plotting the graph (seq length vs. NDCG@100)\n",
    "                if k == 100:\n",
    "                    seq_len = int(len(actual_movies_watched)) + int(x[batch_num].shape[0]) + 1\n",
    "                    if seq_len not in len_to_ndcg_at_100_map: len_to_ndcg_at_100_map[seq_len] = []\n",
    "                    len_to_ndcg_at_100_map[seq_len].append(float(dcg) / float(best))\n",
    "                \n",
    "            total_users += 1.0\n",
    "    \n",
    "    metrics['loss'] = float(metrics['loss']) / float(batch)\n",
    "    metrics['loss'] = round(metrics['loss'], 4)\n",
    "    \n",
    "    for k in Ks:\n",
    "        metrics['NDCG@' + str(k)] = round((100.0 * metrics['NDCG@' + str(k)]) / float(total_users), 4)\n",
    "        metrics['Rec@' + str(k)] = round((100.0 * metrics['Rec@' + str(k)]) / float(total_users), 4)\n",
    "        metrics['Prec@' + str(k)] = round((100.0 * metrics['Prec@' + str(k)]) / float(total_users), 4)\n",
    "        \n",
    "    return metrics, len_to_ndcg_at_100_map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "class Encoder(nn.Module):\n",
    "    def __init__(self, hyper_params):\n",
    "        super(Encoder, self).__init__()\n",
    "        self.linear1 = nn.Linear(\n",
    "            hyper_params['rnn_size'], hyper_params['hidden_size']\n",
    "        )\n",
    "        nn.init.xavier_normal(self.linear1.weight)\n",
    "        self.activation = nn.Tanh()\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.linear1(x)\n",
    "        x = self.activation(x)\n",
    "        return x\n",
    "\n",
    "class Decoder(nn.Module):\n",
    "    def __init__(self, hyper_params):\n",
    "        super(Decoder, self).__init__()\n",
    "        self.linear1 = nn.Linear(hyper_params['latent_size'], hyper_params['hidden_size'])\n",
    "        self.linear2 = nn.Linear(hyper_params['hidden_size'], hyper_params['total_items'])\n",
    "        nn.init.xavier_normal(self.linear1.weight)\n",
    "        nn.init.xavier_normal(self.linear2.weight)\n",
    "        self.activation = nn.Tanh()\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.linear1(x)\n",
    "        x = self.activation(x)\n",
    "        x = self.linear2(x)\n",
    "        return x\n",
    "\n",
    "class Model(nn.Module):\n",
    "    def __init__(self, hyper_params):\n",
    "        super(Model, self).__init__()\n",
    "        self.hyper_params = hyper_params\n",
    "        \n",
    "        self.encoder = Encoder(hyper_params)\n",
    "        self.decoder = Decoder(hyper_params)\n",
    "        \n",
    "        # Since we don't need padding, our vocabulary size = \"hyper_params['total_items']\" and not \"hyper_params['total_items'] + 1\"\n",
    "        self.item_embed = nn.Embedding(hyper_params['total_items'], hyper_params['item_embed_size'])\n",
    "        \n",
    "        self.gru = nn.GRU(\n",
    "            hyper_params['item_embed_size'], hyper_params['rnn_size'], \n",
    "            batch_first = True, num_layers = 1\n",
    "        )\n",
    "        \n",
    "        self.linear1 = nn.Linear(hyper_params['hidden_size'], 2 * hyper_params['latent_size'])\n",
    "        nn.init.xavier_normal(self.linear1.weight)\n",
    "        \n",
    "        self.tanh = nn.Tanh()\n",
    "        \n",
    "    def sample_latent(self, h_enc):\n",
    "        \"\"\"\n",
    "        Return the latent normal sample z ~ N(mu, sigma^2)\n",
    "        \"\"\"\n",
    "        temp_out = self.linear1(h_enc)\n",
    "        \n",
    "        mu = temp_out[:, :self.hyper_params['latent_size']]\n",
    "        log_sigma = temp_out[:, self.hyper_params['latent_size']:]\n",
    "        \n",
    "        sigma = torch.exp(log_sigma)\n",
    "        std_z = torch.from_numpy(np.random.normal(0, 1, size=sigma.size())).float()\n",
    "        if is_cuda_available: std_z = std_z.cuda()\n",
    "\n",
    "        self.z_mean = mu\n",
    "        self.z_log_sigma = log_sigma\n",
    "\n",
    "        return mu + sigma * Variable(std_z, requires_grad=False)  # Reparameterization trick\n",
    "\n",
    "    def forward(self, x):\n",
    "        in_shape = x.shape                                      # [bsz x seq_len] = [1 x seq_len]\n",
    "        x = x.view(-1)                                          # [seq_len]\n",
    "        \n",
    "        x = self.item_embed(x)                                  # [seq_len x embed_size]\n",
    "        x = x.view(in_shape[0], in_shape[1], -1)                # [1 x seq_len x embed_size]\n",
    "        \n",
    "        rnn_out, _ = self.gru(x)                                # [1 x seq_len x rnn_size]\n",
    "        rnn_out = rnn_out.view(in_shape[0] * in_shape[1], -1)   # [seq_len x rnn_size]\n",
    "        \n",
    "        enc_out = self.encoder(rnn_out)                         # [seq_len x hidden_size]\n",
    "        sampled_z = self.sample_latent(enc_out)                 # [seq_len x latent_size]\n",
    "        \n",
    "        dec_out = self.decoder(sampled_z)                       # [seq_len x total_items]\n",
    "        dec_out = dec_out.view(in_shape[0], in_shape[1], -1)    # [1 x seq_len x total_items]\n",
    "                              \n",
    "        return dec_out, self.z_mean, self.z_log_sigma"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Custom loss\n",
    "\n",
    "$$ Loss \\; = \\; \\sum_{u \\in U} Loss_u $$ <br>\n",
    "$$ Loss_u \\; = \\; \\beta * KL( \\, \\phi(z \\vert x) \\, \\Vert \\, {\\rm I\\!N(0, I)} \\, ) \\; - \\; log( \\, P_{\\phi}(g_{\\theta}(x)) \\, ) $$ <br>\n",
    "$ g_{\\theta}(.)$ is the encoder ; $P_{\\phi}(.)$ is the decoded distribution; $ \\beta $ is the anneal factor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "class VAELoss(torch.nn.Module):\n",
    "    def __init__(self, hyper_params):\n",
    "        super(VAELoss,self).__init__()\n",
    "        self.hyper_params = hyper_params\n",
    "\n",
    "    def forward(self, decoder_output, mu_q, logvar_q, y_true_s, anneal):\n",
    "        # Calculate KL Divergence loss\n",
    "        kld = torch.mean(torch.sum(0.5 * (-logvar_q + torch.exp(logvar_q) + mu_q**2 - 1), -1))\n",
    "    \n",
    "        # Calculate Likelihood\n",
    "        dec_shape = decoder_output.shape # [batch_size x seq_len x total_items] = [1 x seq_len x total_items]\n",
    "\n",
    "        decoder_output = F.log_softmax(decoder_output, -1)\n",
    "        num_ones = float(torch.sum(y_true_s[0, 0]))\n",
    "        \n",
    "        likelihood = torch.sum(\n",
    "            -1.0 * y_true_s.view(dec_shape[0] * dec_shape[1], -1) * \\\n",
    "            decoder_output.view(dec_shape[0] * dec_shape[1], -1)\n",
    "        ) / (float(self.hyper_params['batch_size']) * num_ones)\n",
    "        \n",
    "        final = (anneal * kld) + (likelihood)\n",
    "        \n",
    "        return final"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Started reading data file\n",
      "Data Files loaded!\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 435180/435180 [00:00<00:00, 490340.08it/s]\n",
      "100%|██████████| 56578/56578 [00:00<00:00, 836433.31it/s]\n",
      "100%|██████████| 14500/14500 [00:00<00:00, 773463.16it/s]\n",
      "100%|██████████| 54859/54859 [00:00<00:00, 357804.25it/s]\n",
      "100%|██████████| 14097/14097 [00:00<00:00, 813896.01it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "Simulation run on: 2018-11-17 05:02:56.912663\n",
      "\n",
      "\n",
      "Data reading complete!\n",
      "Number of train batches: 4534\n",
      "Number of validation batches:  750\n",
      "Number of test batches:  750\n",
      "Total Items: 3483\n",
      "\n",
      "Model(\n",
      "  (encoder): Encoder(\n",
      "    (linear1): Linear(in_features=200, out_features=150, bias=True)\n",
      "    (activation): Tanh()\n",
      "  )\n",
      "  (decoder): Decoder(\n",
      "    (linear1): Linear(in_features=64, out_features=150, bias=True)\n",
      "    (linear2): Linear(in_features=150, out_features=3483, bias=True)\n",
      "    (activation): Tanh()\n",
      "  )\n",
      "  (item_embed): Embedding(3483, 256)\n",
      "  (gru): GRU(256, 200, batch_first=True)\n",
      "  (linear1): Linear(in_features=150, out_features=128, bias=True)\n",
      "  (tanh): Tanh()\n",
      ")\n",
      "\n",
      "Model Built!\n",
      "Starting Training...\n",
      "\n",
      "| epoch   1 |  1000/ 4534 batches | ms/batch 14.48 | loss 694.2324\n",
      "| epoch   1 |  2000/ 4534 batches | ms/batch 13.54 | loss 602.4391\n",
      "| epoch   1 |  3000/ 4534 batches | ms/batch 13.49 | loss 589.2574\n",
      "| epoch   1 |  4000/ 4534 batches | ms/batch 13.47 | loss 578.7881\n",
      "| epoch   1 |  4534/ 4534 batches | ms/batch 13.80 | loss 591.1757\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   1 | time: 70.33s | loss = 409.2443 | NDCG@10 = 17.6805 | Rec@10 = 11.8539 | Prec@10 = 14.65 | NDCG@100 = 28.5058 | Rec@100 = 45.3319 | Prec@100 = 6.535 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   1 | time: 70.33s | loss = 462.7759 | NDCG@10 = 15.5757 | Rec@10 = 10.8183 | Prec@10 = 12.8267 | NDCG@100 = 26.1487 | Rec@100 = 44.382 | Prec@100 = 6.1347 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   2 |  1000/ 4534 batches | ms/batch 13.87 | loss 574.9784\n",
      "| epoch   2 |  2000/ 4534 batches | ms/batch 13.77 | loss 572.3647\n",
      "| epoch   2 |  3000/ 4534 batches | ms/batch 13.42 | loss 550.4498\n",
      "| epoch   2 |  4000/ 4534 batches | ms/batch 13.77 | loss 578.2275\n",
      "| epoch   2 |  4534/ 4534 batches | ms/batch 13.92 | loss 589.6744\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   2 | time: 70.37s | loss = 399.6062 | NDCG@10 = 18.9326 | Rec@10 = 13.2943 | Prec@10 = 15.9 | NDCG@100 = 29.926 | Rec@100 = 48.4642 | Prec@100 = 6.88 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   2 | time: 70.37s | loss = 452.741 | NDCG@10 = 16.2961 | Rec@10 = 11.8889 | Prec@10 = 13.4 | NDCG@100 = 27.7416 | Rec@100 = 47.1682 | Prec@100 = 6.3707 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   3 |  1000/ 4534 batches | ms/batch 14.50 | loss 578.3138\n",
      "| epoch   3 |  2000/ 4534 batches | ms/batch 13.59 | loss 554.4086\n",
      "| epoch   3 |  3000/ 4534 batches | ms/batch 13.48 | loss 556.5718\n",
      "| epoch   3 |  4000/ 4534 batches | ms/batch 13.44 | loss 544.2934\n",
      "| epoch   3 |  4534/ 4534 batches | ms/batch 13.86 | loss 567.9775\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   3 | time: 70.40s | loss = 394.5773 | NDCG@10 = 20.287 | Rec@10 = 13.7373 | Prec@10 = 16.35 | NDCG@100 = 32.1464 | Rec@100 = 51.5702 | Prec@100 = 7.305 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   3 | time: 70.40s | loss = 448.2275 | NDCG@10 = 16.8153 | Rec@10 = 12.6574 | Prec@10 = 14.0667 | NDCG@100 = 28.2719 | Rec@100 = 47.4969 | Prec@100 = 6.5027 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   4 |  1000/ 4534 batches | ms/batch 13.99 | loss 564.5487\n",
      "| epoch   4 |  2000/ 4534 batches | ms/batch 13.37 | loss 529.4384\n",
      "| epoch   4 |  3000/ 4534 batches | ms/batch 13.86 | loss 551.8424\n",
      "| epoch   4 |  4000/ 4534 batches | ms/batch 13.67 | loss 554.5243\n",
      "| epoch   4 |  4534/ 4534 batches | ms/batch 13.94 | loss 574.3400\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   4 | time: 70.20s | loss = 391.8408 | NDCG@10 = 18.6796 | Rec@10 = 12.8988 | Prec@10 = 15.9 | NDCG@100 = 30.6452 | Rec@100 = 49.9968 | Prec@100 = 7.035 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   4 | time: 70.20s | loss = 446.88 | NDCG@10 = 16.0178 | Rec@10 = 11.8043 | Prec@10 = 13.0533 | NDCG@100 = 27.5351 | Rec@100 = 46.5852 | Prec@100 = 6.2547 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   5 |  1000/ 4534 batches | ms/batch 13.49 | loss 528.3608\n",
      "| epoch   5 |  2000/ 4534 batches | ms/batch 14.01 | loss 567.6543\n",
      "| epoch   5 |  3000/ 4534 batches | ms/batch 13.66 | loss 539.5317\n",
      "| epoch   5 |  4000/ 4534 batches | ms/batch 14.06 | loss 558.9475\n",
      "| epoch   5 |  4534/ 4534 batches | ms/batch 13.56 | loss 543.1979\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   5 | time: 70.56s | loss = 386.6581 | NDCG@10 = 19.3444 | Rec@10 = 13.9431 | Prec@10 = 15.9 | NDCG@100 = 31.8274 | Rec@100 = 51.7872 | Prec@100 = 7.315 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   5 | time: 70.56s | loss = 443.282 | NDCG@10 = 17.3944 | Rec@10 = 12.7725 | Prec@10 = 14.2133 | NDCG@100 = 28.7112 | Rec@100 = 48.2994 | Prec@100 = 6.5293 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   6 |  1000/ 4534 batches | ms/batch 13.95 | loss 547.2315\n",
      "| epoch   6 |  2000/ 4534 batches | ms/batch 13.76 | loss 549.0806\n",
      "| epoch   6 |  3000/ 4534 batches | ms/batch 13.94 | loss 549.2832\n",
      "| epoch   6 |  4000/ 4534 batches | ms/batch 13.80 | loss 539.5304\n",
      "| epoch   6 |  4534/ 4534 batches | ms/batch 13.52 | loss 532.4694\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   6 | time: 70.70s | loss = 386.1627 | NDCG@10 = 20.3743 | Rec@10 = 13.9029 | Prec@10 = 16.25 | NDCG@100 = 33.1925 | Rec@100 = 53.2415 | Prec@100 = 7.435 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   6 | time: 70.70s | loss = 442.529 | NDCG@10 = 17.1881 | Rec@10 = 12.3243 | Prec@10 = 14.2533 | NDCG@100 = 28.7793 | Rec@100 = 48.303 | Prec@100 = 6.5547 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   7 |  1000/ 4534 batches | ms/batch 14.16 | loss 558.4445\n",
      "| epoch   7 |  2000/ 4534 batches | ms/batch 14.04 | loss 560.3086\n",
      "| epoch   7 |  3000/ 4534 batches | ms/batch 13.49 | loss 531.0471\n",
      "| epoch   7 |  4000/ 4534 batches | ms/batch 13.22 | loss 513.8104\n",
      "| epoch   7 |  4534/ 4534 batches | ms/batch 13.82 | loss 547.4234\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   7 | time: 70.33s | loss = 383.7247 | NDCG@10 = 21.02 | Rec@10 = 14.4837 | Prec@10 = 17.3 | NDCG@100 = 32.902 | Rec@100 = 52.6812 | Prec@100 = 7.47 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   7 | time: 70.33s | loss = 442.0416 | NDCG@10 = 16.5444 | Rec@10 = 12.0938 | Prec@10 = 13.7867 | NDCG@100 = 28.4613 | Rec@100 = 48.6207 | Prec@100 = 6.5467 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch   8 |  1000/ 4534 batches | ms/batch 13.28 | loss 507.6781\n",
      "| epoch   8 |  2000/ 4534 batches | ms/batch 13.74 | loss 538.3866\n",
      "| epoch   8 |  3000/ 4534 batches | ms/batch 14.06 | loss 556.3555\n",
      "| epoch   8 |  4000/ 4534 batches | ms/batch 14.07 | loss 559.6968\n",
      "| epoch   8 |  4534/ 4534 batches | ms/batch 13.62 | loss 532.7413\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   8 | time: 70.42s | loss = 381.4067 | NDCG@10 = 21.3885 | Rec@10 = 14.998 | Prec@10 = 17.5 | NDCG@100 = 33.5939 | Rec@100 = 53.4042 | Prec@100 = 7.525 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   8 | time: 70.42s | loss = 439.9853 | NDCG@10 = 17.7856 | Rec@10 = 13.1539 | Prec@10 = 14.7333 | NDCG@100 = 29.0472 | Rec@100 = 48.5316 | Prec@100 = 6.5653 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "| epoch   9 |  1000/ 4534 batches | ms/batch 13.43 | loss 518.2446\n",
      "| epoch   9 |  2000/ 4534 batches | ms/batch 13.51 | loss 522.7480\n",
      "| epoch   9 |  3000/ 4534 batches | ms/batch 14.09 | loss 561.2029\n",
      "| epoch   9 |  4000/ 4534 batches | ms/batch 13.42 | loss 526.4448\n",
      "| epoch   9 |  4534/ 4534 batches | ms/batch 14.20 | loss 575.7377\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   9 | time: 69.96s | loss = 381.5674 | NDCG@10 = 20.2497 | Rec@10 = 13.5328 | Prec@10 = 16.3 | NDCG@100 = 33.2025 | Rec@100 = 53.4998 | Prec@100 = 7.555 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch   9 | time: 69.96s | loss = 440.5537 | NDCG@10 = 17.3369 | Rec@10 = 12.5152 | Prec@10 = 14.3867 | NDCG@100 = 29.0701 | Rec@100 = 48.9783 | Prec@100 = 6.68 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  10 |  1000/ 4534 batches | ms/batch 13.44 | loss 514.4172\n",
      "| epoch  10 |  2000/ 4534 batches | ms/batch 13.75 | loss 528.1638\n",
      "| epoch  10 |  3000/ 4534 batches | ms/batch 13.99 | loss 553.0852\n",
      "| epoch  10 |  4000/ 4534 batches | ms/batch 13.68 | loss 534.6099\n",
      "| epoch  10 |  4534/ 4534 batches | ms/batch 13.99 | loss 556.9785\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  10 | time: 70.29s | loss = 380.7898 | NDCG@10 = 21.6927 | Rec@10 = 15.4119 | Prec@10 = 18.1 | NDCG@100 = 33.1213 | Rec@100 = 53.9105 | Prec@100 = 7.385 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  10 | time: 70.29s | loss = 440.2426 | NDCG@10 = 16.8686 | Rec@10 = 12.807 | Prec@10 = 13.88 | NDCG@100 = 28.7494 | Rec@100 = 48.9745 | Prec@100 = 6.5613 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  11 |  1000/ 4534 batches | ms/batch 13.33 | loss 491.4674\n",
      "| epoch  11 |  2000/ 4534 batches | ms/batch 13.78 | loss 541.0212\n",
      "| epoch  11 |  3000/ 4534 batches | ms/batch 14.05 | loss 561.0723\n",
      "| epoch  11 |  4000/ 4534 batches | ms/batch 13.90 | loss 547.7770\n",
      "| epoch  11 |  4534/ 4534 batches | ms/batch 13.45 | loss 524.4437\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  11 | time: 70.15s | loss = 378.9756 | NDCG@10 = 21.9431 | Rec@10 = 15.3304 | Prec@10 = 18.25 | NDCG@100 = 33.9087 | Rec@100 = 53.3643 | Prec@100 = 7.62 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  11 | time: 70.15s | loss = 440.8863 | NDCG@10 = 17.814 | Rec@10 = 12.7597 | Prec@10 = 14.3333 | NDCG@100 = 29.6719 | Rec@100 = 49.603 | Prec@100 = 6.7013 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  12 |  1000/ 4534 batches | ms/batch 13.90 | loss 537.3446\n",
      "| epoch  12 |  2000/ 4534 batches | ms/batch 13.59 | loss 518.2057\n",
      "| epoch  12 |  3000/ 4534 batches | ms/batch 13.48 | loss 520.7709\n",
      "| epoch  12 |  4000/ 4534 batches | ms/batch 14.07 | loss 550.2935\n",
      "| epoch  12 |  4534/ 4534 batches | ms/batch 13.60 | loss 539.9227\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  12 | time: 70.31s | loss = 378.046 | NDCG@10 = 22.9806 | Rec@10 = 15.409 | Prec@10 = 18.45 | NDCG@100 = 35.4436 | Rec@100 = 55.9399 | Prec@100 = 7.865 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  12 | time: 70.31s | loss = 440.434 | NDCG@10 = 17.3085 | Rec@10 = 12.5238 | Prec@10 = 14.4133 | NDCG@100 = 29.6064 | Rec@100 = 50.3532 | Prec@100 = 6.7987 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  13 |  1000/ 4534 batches | ms/batch 13.97 | loss 538.9647\n",
      "| epoch  13 |  2000/ 4534 batches | ms/batch 14.02 | loss 546.7473\n",
      "| epoch  13 |  3000/ 4534 batches | ms/batch 13.81 | loss 536.6393\n",
      "| epoch  13 |  4000/ 4534 batches | ms/batch 13.59 | loss 527.3986\n",
      "| epoch  13 |  4534/ 4534 batches | ms/batch 12.86 | loss 482.4139\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  13 | time: 70.27s | loss = 378.7246 | NDCG@10 = 20.7518 | Rec@10 = 15.4314 | Prec@10 = 18.0 | NDCG@100 = 33.0697 | Rec@100 = 54.0861 | Prec@100 = 7.575 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  13 | time: 70.27s | loss = 439.9685 | NDCG@10 = 17.9428 | Rec@10 = 13.282 | Prec@10 = 14.64 | NDCG@100 = 29.4205 | Rec@100 = 49.1763 | Prec@100 = 6.66 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  14 |  1000/ 4534 batches | ms/batch 13.41 | loss 501.9060\n",
      "| epoch  14 |  2000/ 4534 batches | ms/batch 13.47 | loss 508.8126\n",
      "| epoch  14 |  3000/ 4534 batches | ms/batch 14.19 | loss 551.7807\n",
      "| epoch  14 |  4000/ 4534 batches | ms/batch 13.94 | loss 546.3706\n",
      "| epoch  14 |  4534/ 4534 batches | ms/batch 14.11 | loss 555.3275\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  14 | time: 70.55s | loss = 377.1391 | NDCG@10 = 21.4106 | Rec@10 = 15.9188 | Prec@10 = 17.3 | NDCG@100 = 33.8904 | Rec@100 = 54.2078 | Prec@100 = 7.56 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  14 | time: 70.55s | loss = 440.0771 | NDCG@10 = 17.6159 | Rec@10 = 12.5782 | Prec@10 = 14.3067 | NDCG@100 = 29.2531 | Rec@100 = 48.9105 | Prec@100 = 6.6467 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  15 |  1000/ 4534 batches | ms/batch 13.81 | loss 531.6418\n",
      "| epoch  15 |  2000/ 4534 batches | ms/batch 13.80 | loss 533.4116\n",
      "| epoch  15 |  3000/ 4534 batches | ms/batch 13.35 | loss 507.6230\n",
      "| epoch  15 |  4000/ 4534 batches | ms/batch 13.58 | loss 531.1563\n",
      "| epoch  15 |  4534/ 4534 batches | ms/batch 13.85 | loss 549.9002\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  15 | time: 69.81s | loss = 375.825 | NDCG@10 = 21.8822 | Rec@10 = 15.4166 | Prec@10 = 18.0 | NDCG@100 = 34.8166 | Rec@100 = 56.1133 | Prec@100 = 7.77 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  15 | time: 69.81s | loss = 440.5926 | NDCG@10 = 17.3926 | Rec@10 = 12.8357 | Prec@10 = 14.2 | NDCG@100 = 29.2527 | Rec@100 = 49.0471 | Prec@100 = 6.7427 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  16 |  1000/ 4534 batches | ms/batch 13.47 | loss 510.0203\n",
      "| epoch  16 |  2000/ 4534 batches | ms/batch 13.77 | loss 534.5903\n",
      "| epoch  16 |  3000/ 4534 batches | ms/batch 13.62 | loss 527.2641\n",
      "| epoch  16 |  4000/ 4534 batches | ms/batch 14.12 | loss 556.5312\n",
      "| epoch  16 |  4534/ 4534 batches | ms/batch 13.03 | loss 496.3910\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  16 | time: 69.81s | loss = 376.1204 | NDCG@10 = 22.5412 | Rec@10 = 15.5416 | Prec@10 = 18.15 | NDCG@100 = 34.7012 | Rec@100 = 54.9658 | Prec@100 = 7.75 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  16 | time: 69.81s | loss = 440.0215 | NDCG@10 = 17.073 | Rec@10 = 12.5878 | Prec@10 = 14.04 | NDCG@100 = 29.0878 | Rec@100 = 49.1162 | Prec@100 = 6.6747 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  17 |  1000/ 4534 batches | ms/batch 14.00 | loss 542.6506\n",
      "| epoch  17 |  2000/ 4534 batches | ms/batch 13.37 | loss 529.9299\n",
      "| epoch  17 |  3000/ 4534 batches | ms/batch 13.82 | loss 510.6635\n",
      "| epoch  17 |  4000/ 4534 batches | ms/batch 13.92 | loss 527.4207\n",
      "| epoch  17 |  4534/ 4534 batches | ms/batch 13.83 | loss 519.5101\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  17 | time: 70.67s | loss = 377.0295 | NDCG@10 = 21.7961 | Rec@10 = 15.8122 | Prec@10 = 18.4 | NDCG@100 = 34.5718 | Rec@100 = 56.0452 | Prec@100 = 7.79 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  17 | time: 70.67s | loss = 440.6775 | NDCG@10 = 17.4694 | Rec@10 = 13.0604 | Prec@10 = 14.3867 | NDCG@100 = 29.1397 | Rec@100 = 48.8987 | Prec@100 = 6.6187 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "| epoch  18 |  1000/ 4534 batches | ms/batch 13.74 | loss 501.9171\n",
      "| epoch  18 |  2000/ 4534 batches | ms/batch 14.10 | loss 534.5383\n",
      "| epoch  18 |  3000/ 4534 batches | ms/batch 13.77 | loss 511.4181\n",
      "| epoch  18 |  4000/ 4534 batches | ms/batch 14.62 | loss 547.6023\n",
      "| epoch  18 |  4534/ 4534 batches | ms/batch 14.18 | loss 541.7166\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  18 | time: 72.20s | loss = 373.5852 | NDCG@10 = 21.7126 | Rec@10 = 15.2227 | Prec@10 = 17.8 | NDCG@100 = 34.5426 | Rec@100 = 55.1844 | Prec@100 = 7.72 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  18 | time: 72.20s | loss = 440.5775 | NDCG@10 = 17.1819 | Rec@10 = 12.2583 | Prec@10 = 13.9067 | NDCG@100 = 29.4199 | Rec@100 = 49.6823 | Prec@100 = 6.7573 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  19 |  1000/ 4534 batches | ms/batch 14.35 | loss 532.0775\n",
      "| epoch  19 |  2000/ 4534 batches | ms/batch 14.05 | loss 544.5360\n",
      "| epoch  19 |  3000/ 4534 batches | ms/batch 12.99 | loss 483.0285\n",
      "| epoch  19 |  4000/ 4534 batches | ms/batch 13.63 | loss 522.9830\n",
      "| epoch  19 |  4534/ 4534 batches | ms/batch 14.12 | loss 556.6161\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  19 | time: 70.53s | loss = 373.3375 | NDCG@10 = 24.1936 | Rec@10 = 16.5911 | Prec@10 = 18.7 | NDCG@100 = 36.0188 | Rec@100 = 55.3671 | Prec@100 = 7.77 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  19 | time: 70.53s | loss = 440.962 | NDCG@10 = 17.5254 | Rec@10 = 12.6883 | Prec@10 = 14.1867 | NDCG@100 = 29.4941 | Rec@100 = 49.371 | Prec@100 = 6.716 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  20 |  1000/ 4534 batches | ms/batch 13.60 | loss 498.7427\n",
      "| epoch  20 |  2000/ 4534 batches | ms/batch 13.08 | loss 495.8008\n",
      "| epoch  20 |  3000/ 4534 batches | ms/batch 13.91 | loss 542.1242\n",
      "| epoch  20 |  4000/ 4534 batches | ms/batch 13.92 | loss 531.5717\n",
      "| epoch  20 |  4534/ 4534 batches | ms/batch 14.55 | loss 577.4531\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  20 | time: 70.28s | loss = 373.5534 | NDCG@10 = 22.8006 | Rec@10 = 17.0722 | Prec@10 = 18.55 | NDCG@100 = 35.6495 | Rec@100 = 56.4905 | Prec@100 = 7.845 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  20 | time: 70.28s | loss = 441.6514 | NDCG@10 = 17.1038 | Rec@10 = 12.6507 | Prec@10 = 13.9733 | NDCG@100 = 29.0753 | Rec@100 = 49.0529 | Prec@100 = 6.6 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  21 |  1000/ 4534 batches | ms/batch 13.85 | loss 513.6595\n",
      "| epoch  21 |  2000/ 4534 batches | ms/batch 13.71 | loss 519.3936\n",
      "| epoch  21 |  3000/ 4534 batches | ms/batch 13.83 | loss 526.5237\n",
      "| epoch  21 |  4000/ 4534 batches | ms/batch 13.92 | loss 524.3658\n",
      "| epoch  21 |  4534/ 4534 batches | ms/batch 14.02 | loss 539.1212\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  21 | time: 70.73s | loss = 372.9583 | NDCG@10 = 25.0606 | Rec@10 = 17.5849 | Prec@10 = 20.05 | NDCG@100 = 36.6842 | Rec@100 = 56.2753 | Prec@100 = 7.985 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  21 | time: 70.73s | loss = 440.9448 | NDCG@10 = 17.6607 | Rec@10 = 12.6864 | Prec@10 = 14.4133 | NDCG@100 = 29.441 | Rec@100 = 49.2637 | Prec@100 = 6.6773 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  22 |  1000/ 4534 batches | ms/batch 13.46 | loss 491.8200\n",
      "| epoch  22 |  2000/ 4534 batches | ms/batch 13.97 | loss 531.2563\n",
      "| epoch  22 |  3000/ 4534 batches | ms/batch 14.03 | loss 532.4067\n",
      "| epoch  22 |  4000/ 4534 batches | ms/batch 13.60 | loss 515.6356\n",
      "| epoch  22 |  4534/ 4534 batches | ms/batch 14.08 | loss 556.8879\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  22 | time: 70.58s | loss = 374.7062 | NDCG@10 = 21.9737 | Rec@10 = 15.7166 | Prec@10 = 17.75 | NDCG@100 = 35.196 | Rec@100 = 55.9714 | Prec@100 = 7.795 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  22 | time: 70.58s | loss = 442.1011 | NDCG@10 = 17.0824 | Rec@10 = 12.7752 | Prec@10 = 14.0267 | NDCG@100 = 28.8794 | Rec@100 = 49.0702 | Prec@100 = 6.5787 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  23 |  1000/ 4534 batches | ms/batch 14.05 | loss 530.0608\n",
      "| epoch  23 |  2000/ 4534 batches | ms/batch 14.09 | loss 535.5466\n",
      "| epoch  23 |  3000/ 4534 batches | ms/batch 13.99 | loss 527.6722\n",
      "| epoch  23 |  4000/ 4534 batches | ms/batch 13.42 | loss 495.7750\n",
      "| epoch  23 |  4534/ 4534 batches | ms/batch 13.72 | loss 514.8091\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  23 | time: 70.82s | loss = 371.5749 | NDCG@10 = 22.9648 | Rec@10 = 16.2642 | Prec@10 = 19.2 | NDCG@100 = 35.0067 | Rec@100 = 55.5697 | Prec@100 = 7.865 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  23 | time: 70.82s | loss = 441.4975 | NDCG@10 = 17.7443 | Rec@10 = 12.5617 | Prec@10 = 14.2933 | NDCG@100 = 29.5124 | Rec@100 = 49.1954 | Prec@100 = 6.768 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  24 |  1000/ 4534 batches | ms/batch 14.62 | loss 514.3072\n",
      "| epoch  24 |  2000/ 4534 batches | ms/batch 13.74 | loss 526.3544\n",
      "| epoch  24 |  3000/ 4534 batches | ms/batch 13.44 | loss 516.0305\n",
      "| epoch  24 |  4000/ 4534 batches | ms/batch 13.75 | loss 535.4107\n",
      "| epoch  24 |  4534/ 4534 batches | ms/batch 13.06 | loss 505.8145\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  24 | time: 70.36s | loss = 371.5445 | NDCG@10 = 23.2687 | Rec@10 = 15.9934 | Prec@10 = 18.8 | NDCG@100 = 35.8741 | Rec@100 = 56.7194 | Prec@100 = 7.935 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  24 | time: 70.36s | loss = 441.636 | NDCG@10 = 17.0421 | Rec@10 = 12.8897 | Prec@10 = 13.8667 | NDCG@100 = 29.2889 | Rec@100 = 49.5786 | Prec@100 = 6.6493 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| epoch  25 |  1000/ 4534 batches | ms/batch 14.13 | loss 514.4130\n",
      "| epoch  25 |  2000/ 4534 batches | ms/batch 13.61 | loss 486.9359\n",
      "| epoch  25 |  3000/ 4534 batches | ms/batch 13.87 | loss 520.6551\n",
      "| epoch  25 |  4000/ 4534 batches | ms/batch 14.13 | loss 536.7007\n",
      "| epoch  25 |  4534/ 4534 batches | ms/batch 14.45 | loss 563.1788\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  25 | time: 71.54s | loss = 371.7695 | NDCG@10 = 22.2845 | Rec@10 = 16.0939 | Prec@10 = 18.5 | NDCG@100 = 35.545 | Rec@100 = 56.4588 | Prec@100 = 7.98 (TRAIN)\n",
      "-----------------------------------------------------------------------------------------\n",
      "| end of epoch  25 | time: 71.54s | loss = 442.4591 | NDCG@10 = 17.8469 | Rec@10 = 13.0326 | Prec@10 = 14.4667 | NDCG@100 = 29.523 | Rec@100 = 48.9586 | Prec@100 = 6.5907 (VAL)\n",
      "-----------------------------------------------------------------------------------------\n"
     ]
    },
    {
     "data": {
      "image/png": "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svFeo1VQUOCrVq1KOow6O+qoWL5p6tSkI2mE1q+PlZv/8594gnfYIemIRERERJo8M1vt7gXVHPwjUWpUArQG2gLrUtva1KV2AObhvkul6zYHluPeriHi1ghWHiguhrff1vTABtOyJTz+uFq3i4iIiOQL99/g3gX3bkTvhtdx3xL37XDvltq/+tvkymz7Ctc+gcqNMTJICVYeUP1VFlRs3X7TTUlHIyIiIiKZdRlmMzD7ALgMvtvvIVM0RTAP/OEP8NvfwrJl0LFj0tE0Yu5wxhkwcyZMnAitWiUdkYiIiEiTVeMUwRymBCsPDBoEn32m+qusKCqCFi1g882TjkRERESkScvXBEtTBHNcuv7q8MOTjqSJaNs2kqtVq+Chh5KORkRERETyjBKsHDdxIqxerfqrrLv3XvjRj9S6XURERETqRAlWjtP6Vwm59FI46CC48EJYuDDpaEREREQkTyjBynFjx8I++8DWWycdSRNTsXX7sGFq3S4iIiIitaIEK4etXw9vvaXpgYlJt25/4w249dakoxERERGRPNAi6QCkeun6KzW4SNCwYbBgAZx6atKRiIiIiEgeUIKVw1R/lQPM4P/9v/jZPYYVtT6WiIiIiFRDUwRz2NixsPfesM02SUcilJXBKafARRclHYmIiIiI5DAlWDlK9Vc5plkz2GsvePBB+Pvfk45GRERERHKUEqwcNWlSrHWr+qsccs010br9ggvUul1EREREqqQEK0el66+UYOWQdOv2khK1bhcRERGRKinBylGFhTEjTfVXOWbnneHPf4bZs2HevKSjEREREZEcowQrB61fD2+/rfqrnHX22ZFg7borfPQRXHwx/Otf0VNfRERERJo0JVg5aPJk+OYbJVg5ywzatYufp0+HRx+FH/wAttoKjj02FicuKko2RhERERFJhBKsHKT1r/LIkCHw5Zfw0kvR/GLOHLjssqjTAnjzzei3v359snGKiIiISFaYuycdQ1YVFBT4qlWrkg6jRsccAwsWwMyZSUcideYOn3wCO+wQ5wcNgldeiRGvwYPhuOPiF7z11snGKSIiIpLjzGy1uxckHUddaQQrx5SUaP2rvGZWnlwB/OMf8PTTcPLJ8MYbcM45cOqp5cfnzImkTEREREQahRZJByAbUv1VI9O2Lfzwh7GVlcUveN26OLZiRbSK3HbbqN067jgYOBC22CLZ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\n",
      "text/plain": [
       "<Figure size 864x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=========================================================================================\n",
      "| End of training | loss = 424.4492 | NDCG@10 = 17.1436 | Rec@10 = 13.0244 | Prec@10 = 14.3333 | NDCG@100 = 29.7925 | Rec@100 = 49.9151 | Prec@100 = 6.9427 (TEST)\n",
      "=========================================================================================\n",
      "average runtime per epoch = 70.4868 s\n"
     ]
    }
   ],
   "source": [
    "def train(reader):\n",
    "    model.train()\n",
    "    total_loss = 0\n",
    "    start_time = time.time()\n",
    "    batch = 0\n",
    "    batch_limit = int(train_reader.num_b)\n",
    "    total_anneal_steps = 200000\n",
    "    anneal = 0.0\n",
    "    update_count = 0.0\n",
    "    anneal_cap = 0.2\n",
    "\n",
    "    for x, y_s in reader.iter():\n",
    "        batch += 1\n",
    "        \n",
    "        # Empty the gradients\n",
    "        model.zero_grad()\n",
    "        optimizer.zero_grad()\n",
    "    \n",
    "        # Forward pass\n",
    "        decoder_output, z_mean, z_log_sigma = model(x)\n",
    "        \n",
    "        # Backward pass\n",
    "        loss = criterion(decoder_output, z_mean, z_log_sigma, y_s, anneal)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        total_loss += loss.data\n",
    "        \n",
    "        # Anneal logic\n",
    "        if total_anneal_steps > 0:\n",
    "            anneal = min(anneal_cap, 1. * update_count / total_anneal_steps)\n",
    "        else:\n",
    "            anneal = anneal_cap\n",
    "        update_count += 1.0\n",
    "        \n",
    "        # Logging mechanism\n",
    "        if (batch % hyper_params['batch_log_interval'] == 0 and batch > 0) or batch == batch_limit:\n",
    "            div = hyper_params['batch_log_interval']\n",
    "            if batch == batch_limit: div = (batch_limit % hyper_params['batch_log_interval']) - 1\n",
    "            if div <= 0: div = 1\n",
    "\n",
    "            cur_loss = (total_loss[0] / div)\n",
    "            elapsed = time.time() - start_time\n",
    "            \n",
    "            ss = '| epoch {:3d} | {:5d}/{:5d} batches | ms/batch {:5.2f} | loss {:5.4f}'.format(\n",
    "                    epoch, batch, batch_limit, (elapsed * 1000) / div, cur_loss\n",
    "            )\n",
    "            \n",
    "            file_write(hyper_params['log_file'], ss)\n",
    "\n",
    "            total_loss = 0\n",
    "            start_time = time.time()\n",
    "\n",
    "# Train It..\n",
    "train_reader, val_reader, test_reader, total_items = load_data(hyper_params)\n",
    "hyper_params['total_items'] = total_items\n",
    "hyper_params['testing_batch_limit'] = test_reader.num_b\n",
    "\n",
    "file_write(hyper_params['log_file'], \"\\n\\nSimulation run on: \" + str(dt.datetime.now()) + \"\\n\\n\")\n",
    "file_write(hyper_params['log_file'], \"Data reading complete!\")\n",
    "file_write(hyper_params['log_file'], \"Number of train batches: {:4d}\".format(train_reader.num_b))\n",
    "file_write(hyper_params['log_file'], \"Number of validation batches: {:4d}\".format(val_reader.num_b))\n",
    "file_write(hyper_params['log_file'], \"Number of test batches: {:4d}\".format(test_reader.num_b))\n",
    "file_write(hyper_params['log_file'], \"Total Items: \" + str(total_items) + \"\\n\")\n",
    "\n",
    "model = Model(hyper_params)\n",
    "if is_cuda_available: model.cuda()\n",
    "\n",
    "criterion = VAELoss(hyper_params)\n",
    "\n",
    "if hyper_params['optimizer'] == 'adagrad':\n",
    "    optimizer = torch.optim.Adagrad(\n",
    "        model.parameters(), weight_decay=hyper_params['weight_decay'], lr = hyper_params['learning_rate']\n",
    "    )\n",
    "elif hyper_params['optimizer'] == 'adadelta':\n",
    "    optimizer = torch.optim.Adadelta(\n",
    "        model.parameters(), weight_decay=hyper_params['weight_decay']\n",
    "    )\n",
    "elif hyper_params['optimizer'] == 'adam':\n",
    "    optimizer = torch.optim.Adam(\n",
    "        model.parameters(), weight_decay=hyper_params['weight_decay']\n",
    "    )\n",
    "elif hyper_params['optimizer'] == 'rmsprop':\n",
    "    optimizer = torch.optim.RMSprop(\n",
    "        model.parameters(), weight_decay=hyper_params['weight_decay']\n",
    "    )\n",
    "\n",
    "file_write(hyper_params['log_file'], str(model))\n",
    "file_write(hyper_params['log_file'], \"\\nModel Built!\\nStarting Training...\\n\")\n",
    "\n",
    "best_val_ndcg = None\n",
    "\n",
    "try:\n",
    "    for epoch in range(1, hyper_params['epochs'] + 1):\n",
    "        epoch_start_time = time.time()\n",
    "        \n",
    "        train(train_reader)\n",
    "        \n",
    "        # Calulating the metrics on the train set\n",
    "        metrics, _ = evaluate(model, criterion, train_reader, hyper_params, True)\n",
    "        string = \"\"\n",
    "        for m in metrics: string += \" | \" + m + ' = ' + str(metrics[m])\n",
    "        string += ' (TRAIN)'\n",
    "    \n",
    "        # Calulating the metrics on the validation set\n",
    "        metrics, _ = evaluate(model, criterion, val_reader, hyper_params, False)\n",
    "        string2 = \"\"\n",
    "        for m in metrics: string2 += \" | \" + m + ' = ' + str(metrics[m])\n",
    "        string2 += ' (VAL)'\n",
    "\n",
    "        ss  = '-' * 89\n",
    "        ss += '\\n| end of epoch {:3d} | time: {:5.2f}s'.format(epoch, (time.time() - epoch_start_time))\n",
    "        ss += string\n",
    "        ss += '\\n'\n",
    "        ss += '-' * 89\n",
    "        ss += '\\n| end of epoch {:3d} | time: {:5.2f}s'.format(epoch, (time.time() - epoch_start_time))\n",
    "        ss += string2\n",
    "        ss += '\\n'\n",
    "        ss += '-' * 89\n",
    "        file_write(hyper_params['log_file'], ss)\n",
    "        \n",
    "        if not best_val_ndcg or metrics['NDCG@100'] >= best_val_ndcg:\n",
    "            with open(hyper_params['model_file_name'], 'wb') as f: torch.save(model, f)\n",
    "            best_val_ndcg = metrics['NDCG@100']\n",
    "\n",
    "except KeyboardInterrupt: print('Exiting from training early')\n",
    "\n",
    "# Plot Traning graph\n",
    "f = open(model.hyper_params['log_file'])\n",
    "lines = f.readlines()\n",
    "lines.reverse()\n",
    "\n",
    "train = []\n",
    "test = []\n",
    "\n",
    "for line in lines:\n",
    "    if line[:10] == 'Simulation' and len(train) > 1: break\n",
    "    elif line[:10] == 'Simulation' and len(train) <= 1: train, test = [], []\n",
    "        \n",
    "    if line[2:5] == 'end' and line[-5:-2] == 'VAL': test.append(line.strip().split(\"|\"))\n",
    "    elif line[2:5] == 'end' and line[-7:-2] == 'TRAIN': train.append(line.strip().split(\"|\"))\n",
    "\n",
    "train.reverse()\n",
    "test.reverse()\n",
    "\n",
    "train_ndcg = []\n",
    "test_ndcg = []\n",
    "test_loss, train_loss = [], []\n",
    "\n",
    "for i in train:\n",
    "    for metric in i:\n",
    "        if metric.split(\"=\")[0] == \" NDCG@100 \":\n",
    "            train_ndcg.append(float(metric.split('=')[1].split(' ')[1]))\n",
    "        if metric.split(\"=\")[0] == \" loss \":\n",
    "            train_loss.append(float(metric.split(\"=\")[1].split(' ')[1]))\n",
    "\n",
    "total, avg_runtime = 0.0, 0.0\n",
    "for i in test:\n",
    "    avg_runtime += float(i[2].split(\" \")[2][:-1])\n",
    "    total += 1.0\n",
    "    \n",
    "    for metric in i:\n",
    "        if metric.split(\"=\")[0] == \" NDCG@100 \":\n",
    "            test_ndcg.append(float(metric.split('=')[1].split(' ')[1]))\n",
    "        if metric.split(\"=\")[0] == \" loss \":\n",
    "            test_loss.append(float(metric.split(\"=\")[1].split(' ')[1]))\n",
    "\n",
    "fig, ax1 = plt.subplots(figsize=(12, 5))\n",
    "ax1.set_title(hyper_params[\"project_name\"],fontweight=\"bold\", size=20)\n",
    "ax1.plot(test_ndcg, 'b-')\n",
    "ax1.set_xlabel('Epochs', fontsize = 20.0)\n",
    "ax1.set_ylabel('NDCG@100', color='b', fontsize = 20.0)\n",
    "ax1.tick_params('y', colors='b')\n",
    "\n",
    "ax2 = ax1.twinx()\n",
    "ax2.plot(test_loss, 'r--')\n",
    "ax2.set_ylabel('Loss', color='r')\n",
    "ax2.tick_params('y', colors='r')\n",
    "\n",
    "fig.tight_layout()\n",
    "if not os.path.isdir(\"saved_plots/\"): os.mkdir(\"saved_plots/\")\n",
    "fig.savefig(\"saved_plots/learning_curve_\" + hyper_params[\"project_name\"] + \".pdf\")\n",
    "plt.show()\n",
    "\n",
    "# Checking metrics for the test set on best saved model\n",
    "with open(hyper_params['model_file_name'], 'rb') as f: model = torch.load(f)\n",
    "metrics, len_to_ndcg_at_100_map = evaluate(model, criterion, test_reader, hyper_params, False)\n",
    "\n",
    "# Plot sequence length vs NDCG@100 graph\n",
    "plot_len_vs_ndcg(len_to_ndcg_at_100_map)\n",
    "\n",
    "string = \"\"\n",
    "for m in metrics: string += \" | \" + m + ' = ' + str(metrics[m])\n",
    "\n",
    "ss  = '=' * 89\n",
    "ss += '\\n| End of training'\n",
    "ss += string + \" (TEST)\"\n",
    "ss += '\\n'\n",
    "ss += '=' * 89\n",
    "file_write(hyper_params['log_file'], ss)\n",
    "print(\"average runtime per epoch =\", round(avg_runtime / float(total), 4), \"s\")"
   ]
  }
 ],
 "metadata": {
  "kernel_info": {
   "name": "python2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  },
  "nteract": {
   "version": "0.12.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {
    "height": "223px",
    "width": "193px"
   },
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Notebook contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "226px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
